﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Shapes;
using Silvermoon.OpenGL;
using System.Drawing;
using Silvermoon.UI;
using Silvermoon.Classes;

namespace Silvermoon.Controls
{
    public enum TDIHighlightMode
    {
        Title = 1,
        Details = 2,
        TitleAndDetails = 3
    }

    /// <summary>
    /// Item with image, title and details.
    /// </summary>
    public class TitleDetailsImageItem : VirtualItem
    {
        #region fields

        const int TITLE_FONT_NOT_NULL = 8;
        const int DETAILS_FONT_NOT_NULL = 16;

        private bool autoHeight = false;
        private Alignment titleLineAlignment = Alignment.Near;
        protected Shape imageBackground;
        protected ImageShape imageShape;
        protected HighlightStringShape titleShape;
        protected HighlightStringShape detailsShape;
        protected Margin padding = new Margin(8);
        protected const int titleDetailsSpace = 2;
        private TDIHighlightMode highlightMode = TDIHighlightMode.TitleAndDetails;

        #endregion
        #region ctors/dtors

        public TitleDetailsImageItem()
            : base()
        {
            imageShape = new ImageShape { Size = new Size(64, 64), UseTextColor = true, Color = Color.Empty };
            titleShape = new HighlightStringShape { Alignment = Alignment.Near, LineAlignment = Alignment.Near, LineMode = LineMode.SingleLine };
            detailsShape = new HighlightStringShape { Opacity = 200, FontScale = 0.75f, Alignment = Alignment.Near, LineAlignment = Alignment.Near, LineMode = LineMode.WordWrap };

            Shapes.AddRange(imageShape, titleShape, detailsShape);
        }


        public TitleDetailsImageItem(string title, string details)
            : this()
        {
            this.Title = title;
            this.Details = details;
        }

        public TitleDetailsImageItem(string title, string details, Texture image)
            : this()
        {
            this.Title = title;
            this.Details = details;
            this.Image = image;
        }

        public TitleDetailsImageItem(string title)
            : this()
        {
            this.Title = title;
        }


        #endregion
        #region accessors

        public static PropertyAccessor<TitleDetailsImageItem, Color> ImageColorProperty = PropertyAccessor.Register<TitleDetailsImageItem, Color>("ImageColor",
            c => c.ImageColor,
            (c, v) => c.ImageColor = v);

        public static PropertyAccessor<TitleDetailsImageItem, Size> ImageSizeroperty = PropertyAccessor.Register<TitleDetailsImageItem, Size>("ImageSize",
            c => c.ImageSize,
            (c, v) => c.ImageSize = v);

        public static PropertyAccessor<TitleDetailsImageItem, int> ImageOpacityProperty = PropertyAccessor.Register<TitleDetailsImageItem, int>("ImageOpacity",
            c => c.ImageOpacity, (c, v) => c.ImageOpacity = v);

        #endregion
        #region props

        public int ImageOpacity
        {
            get { return imageShape.Opacity; }
            set
            {
                if (imageShape.Opacity != value)
                {
                    imageShape.Opacity = value;
                    Invalidate(InvalidationFlag.Appearance);
                }
            }
        }

        public bool AutoHeight
        {
            get { return autoHeight; }
            set
            {
                if (autoHeight != value)
                {
                    autoHeight = value;
                    Invalidate(InvalidationFlag.Layout);
                }
            }
        }

        public Alignment TitleLineAlignment
        {
            get { return titleLineAlignment; }
            set
            {
                if (titleLineAlignment != value)
                {
                    titleLineAlignment = value;
                    OnTitleLineAlignmentChanged();
                }
            }
        }


        public Color HighlightTextColor
        {
            get { return titleShape.HighlightTextColor; }
            set
            {
                if (titleShape.HighlightTextColor != value)
                {
                    titleShape.HighlightTextColor = value;
                    detailsShape.HighlightTextColor = value;
                    OnHightlightTextColorChanged();
                }
            }
        }


        public Shape ImageBackground
        {
            get { return imageBackground; }
            set
            {
                if (imageBackground != value)
                {
                    Shapes.Remove(imageBackground);
                    DisposeObject(imageBackground);
                    imageBackground = value;
                    if (value != null)
                    {
                        Shapes.Prepend(imageShape, imageBackground);
                    }
                    OnImageBackgroundChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the margin of the inner elements.
        /// </summary>
        public Margin Padding
        {
            get { return padding ?? Margin.Empty; }
            set
            {
                if (!Padding.Equals(value))
                {
                    padding = value;
                    OnPaddingChanged();
                }
            }
        }

        public Size ImageSize
        {
            get { return imageShape.Size; }
            set
            {
                if (imageShape.Size != value)
                {
                    imageShape.Size = value;
                    Invalidate(InvalidationFlag.Layout);
                }
            }
        }

        public FontSprite TitleFont
        {
            get { return titleShape.Font; }
            set
            {
                bitmask |= TITLE_FONT_NOT_NULL;
                if (titleShape.Font != value)
                {
                    titleShape.Font = value;
                    OnTitleFontChanged();
                }
            }
        }

        public FontSprite DetailsFont
        {
            get { return detailsShape.Font; }
            set
            {
                bitmask |= DETAILS_FONT_NOT_NULL;
                if (titleShape.Font != value)
                {
                    detailsShape.Font = value;
                    OnDetailsFontChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the title. Title is displayed in a single line.
        /// </summary>
        public string Title
        {
            get { return titleShape.Text ?? string.Empty; }
            set
            {
                if (titleShape.Text != value)
                {
                    titleShape.Text = value;
                    OnTitleChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the details. Details is word wrapped if necassary and fills the space under the title.
        /// </summary>
        public string Details
        {
            get { return detailsShape.Text ?? string.Empty; }
            set
            {
                if (detailsShape.Text != value)
                {
                    detailsShape.Text = value;
                    OnDetailsChanged();
                }
            }
        }

        /// <summary>
        /// Gets or sets the image. The image appears at the left.
        /// </summary>
        public Texture Image
        {
            get { return imageShape.Image; }
            set
            {
                if (imageShape.Image != value)
                {
                    imageShape.Image = value;
                    OnImageChanged();
                }
            }
        }

        public Color ImageColor
        {
            get { return imageShape.Color; }
            set
            {
                if (ImageColor != value)
                {
                    imageShape.Color = value;
                    Invalidate(InvalidationFlag.Color);
                }
            }
        }

        #endregion
        #region methods

        protected override void ApplyStyle()
        {
            base.ApplyStyle();
            if ((bitmask & TITLE_FONT_NOT_NULL) == 0) TitleFont = SystemFonts.Medium;
            if ((bitmask & DETAILS_FONT_NOT_NULL) == 0) DetailsFont = SystemFonts.Normal;
        }

        protected override void OnHostChanged()
        {
            base.OnHostChanged();
            if (Host != null)
            {
                if (autoHeight) Host.SetItemHeight(this.ItemIndex, Height + Margin.Height);
            }
        }

        protected override void OnSizeChanging(Size oldValue)
        {
            if (AutoHeight && oldValue.Height != Height)
            {
                NotifyHeightChanged();
            }
            base.OnSizeChanging(oldValue);
        }


        private void NotifyHeightChanged()
        {
            IItemHost listView = Host;
            if (Width > 0 && listView != null && listView.IsDynamicItemHeightSupported)
            {
                listView.SetItemHeight(this.ItemIndex, Height + Margin.Height);
            }
        }


        protected virtual void OnTitleLineAlignmentChanged()
        {
            Invalidate(InvalidationFlag.Layout);
        }

        protected virtual void OnImageBackgroundChanged()
        {
            Invalidate(InvalidationFlag.LayoutAppearance);
        }

        protected virtual void OnTitleFontChanged()
        {
            Invalidate(InvalidationFlag.LayoutAppearance);
        }

        protected virtual void OnDetailsFontChanged()
        {
            Invalidate(InvalidationFlag.LayoutAppearance);
        }

        protected virtual void OnPaddingChanged()
        {
            Invalidate(InvalidationFlag.Layout);
        }

        protected virtual void OnImageChanged()
        {
            Invalidate(InvalidationFlag.Appearance);
        }

        protected virtual void OnDetailsChanged()
        {
            Invalidate(InvalidationFlag.Appearance);
        }

        protected virtual void OnTitleChanged()
        {
            Invalidate(InvalidationFlag.Appearance);
        }

        protected override void OnLayout()
        {
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Layout)) LayoutShapes();
            base.OnLayout();
        }

        protected virtual void LayoutShapes()
        {
            Rectangle bounds = padding.LayoutChild(ClientBounds);
            int h = bounds.Height;

            imageShape.Location = Control.ScaledPoint(bounds.X, bounds.Y + 8);
            int x = Image != null ? (imageShape.Width + padding.LeftZero) : 0;
            int w = Math.Max(0, bounds.Width - x);
            if (imageBackground != null) imageBackground.Bounds = imageShape.Bounds;

            int titleHeight = Title.Length > 0 ? titleShape.Font.CharHeight : 0;
            int y = GetTiteLineTop(bounds.Y, bounds.Height, titleHeight);
            Rectangle titleBounds = Control.ScaledRect(bounds.X + x, y, w, titleHeight);
            titleShape.Bounds = titleBounds;

            y = titleBounds.Bottom + ((!string.IsNullOrEmpty(Title)) ? titleDetailsSpace : 0);
            int dh = autoHeight ? 819200 : Math.Max(0, h - titleHeight);
            detailsShape.Bounds = Control.ScaledRect(bounds.X + x, y, w, dh);

            if (autoHeight)
            {
                h = Math.Max(Height, detailsShape.Y + detailsShape.TextSize.Height - Margin.TopZero);
                Height = h;
            }
        }

        private int GetTiteLineTop(int top, int height, int titleHeight)
        {
            switch (titleLineAlignment)
            {
                case Alignment.Near: return top;
                case Alignment.Center: return (height - titleHeight) / 2 + top;
                case Alignment.Far: return height - titleHeight + top;
                default: throw new NotSupportedException();
            }
        }

        #endregion
        #region Highlightning methods


        public TDIHighlightMode HighlightMode
        {
            get { return highlightMode; }
            set
            {
                if (highlightMode != value)
                {
                    highlightMode = value;
                    OnHighlightModeChanged();
                }
            }
        }

        protected virtual void OnHighlightModeChanged()
        {
            Invalidate(InvalidationFlag.Text);
        }

        /// <summary>
        /// Highlights all appearances of a given text.
        /// </summary>
        /// <param name="subString">text to highlight.</param>
        public void Highlight(string subString)
        {
            if ((highlightMode & TDIHighlightMode.Title) != 0) titleShape.Highlight(subString); else titleShape.ClearHighlights();
            if ((highlightMode & TDIHighlightMode.Details) != 0) detailsShape.Highlight(subString); else detailsShape.ClearHighlights();
            Invalidate(InvalidationFlag.Text);
        }

        protected virtual void OnHightlightTextColorChanged()
        {
            Invalidate(InvalidationFlag.Text);
        }


        #endregion
    }
}
